import { hasOwn } from "./tools";

export default class Calc {
  constructor(failoverDist, keyMaps) {
    this.failoverDist = failoverDist;
    this.keyMaps = keyMaps;
  }

  /**
   * 计算焦点元素到某点的最短距离
   * @param {Number} x 焦点left
   * @param {Number} y 焦点top
   * @param {Number} w 焦点width
   * @param {Number} h 焦点height
   * @param {Number} a 基准点x
   * @param {Number} b 基准点y
   * @returns Number
   */
  rulesSurfaceToPoint(x, y, w, h, a, b) {
    if (a < x) {
      if (b < y) {
        return Math.sqrt(Math.pow(x - a, 2) + Math.pow(y - b, 2));
      } else if (b >= y && b <= y + h) {
        return x - a;
      } else {
        return Math.sqrt(Math.pow(x - a, 2) + Math.pow(b - y - h, 2));
      }
    } else if (a > x + w) {
      if (b < y) {
        return Math.sqrt(Math.pow(a - x - w, 2) + Math.pow(y - b, 2));
      } else if (b >= y && b <= y + h) {
        return a - x;
      } else {
        return Math.sqrt(Math.pow(a - x - w, 2) + Math.pow(b - y - h, 2));
      }
    } else {
      if (b < y) {
        return y - b;
      } else if (b >= y && b <= y + h) {
        return 0;
      } else {
        return b - y - h;
      }
    }
  }

  /**
   * 对比返回距离x,y坐标更近的焦点
   * @param {Number} x 基准点x
   * @param {Number} y 基准点y
   * @param {Object} target 下一个焦点
   * @param {Object|null} cache 缓存焦点对象
   * @returns Object
   */
  rulesPoint(x, y, target, cache) {
    if (cache) {
      let { top: nt, left: nl, width: nw, height: nh } = target;
      let { top, left, width, height } = cache;
      let nextDist = this.rulesSurfaceToPoint(nl, nt, nw, nh, x, y);
      let prevDist = this.rulesSurfaceToPoint(left, top, width, height, x, y);
      if (nextDist < prevDist) {
        return target;
      } else if (nextDist === prevDist) {
        if (nt > top || nl > left) {
          return target;
        } else {
          return cache;
        }
      } else {
        return cache;
      }
    } else {
      return target;
    }
  }

  /**
   * 判断元素某一方向上是否交错
   * 参数取两个元素同一方向上的起点及相应尺寸
   * @param {Number} pb prev元素起点
   * @param {Number} pc prev元素相应方向的尺寸
   * @param {Number} nb next元素起点
   * @param {Number} nc next元素相应方向的尺寸
   */
  getOverlay(pb, pc, nb, nc) {
    if (nb >= pb + pc || nb + nc <= pb) {
      return 0;
    } else {
      let nd = nb + nc;
      let pd = pb + pc;
      if (nd > pd) {
        return Math.min(pd - nb, pc);
      } else if (nd === pd) {
        return Math.min(nc, pc);
      } else {
        return Math.min(nd - pb, nc);
      }
    }
  }

  /**
   * 通用元素焦点移动计算规则
   * @param {Object} point 焦点元素对象
   * @param {Object} target 待做对比的焦点元素对象
   * @param {Object|null} cache 满足条件的最优的焦点元素对象
   * @param {String} dir 焦点移动方向
   */
  rules(point, target, cache, dir) {
    let {
      left: nextLeft,
      top: nextTop,
      width: nextWidth,
      height: nextHeight,
    } = target;
    if (!nextWidth || !nextHeight) {
      return cache;
    }
    let temp = null;
    let { top, left, width, height } = point;
    if (dir === "left") {
      // left值小于焦点元素, 且left、top差值均最小, 且水平方向有交集
      let nextOverlay = this.getOverlay(top, height, nextTop, nextHeight);
      if (nextLeft + nextWidth <= left && nextOverlay) {
        if (cache) {
          let { left: prevLeft, top: prevTop, height: prevHeight } = cache;
          if (prevLeft > nextLeft) {
            temp = cache;
          } else if (prevLeft === nextLeft) {
            let prevOverlay = this.getOverlay(top, height, prevTop, prevHeight);
            if (prevOverlay >= nextOverlay) {
              temp = cache;
            } else {
              temp = target;
            }
          } else {
            temp = target;
          }
        } else {
          temp = target;
        }
      }
    } else if (dir === "right") {
      // left值大于焦点元素, 且left、top差值均最小, 且水平方向有交集
      let nextOverlay = this.getOverlay(top, height, nextTop, nextHeight);
      if (nextLeft >= left + width && nextOverlay) {
        if (cache) {
          let { left: prevLeft, top: prevTop, height: prevHeight } = cache;
          if (prevLeft < nextLeft) {
            temp = cache;
          } else if (prevLeft === nextLeft) {
            let prevOverlay = this.getOverlay(top, height, prevTop, prevHeight);
            if (prevOverlay >= nextOverlay) {
              temp = cache;
            } else {
              temp = target;
            }
          } else {
            temp = target;
          }
        } else {
          temp = target;
        }
      }
    } else if (dir === "up") {
      // top值小于焦点元素, 水平方向无交集
      let targetPosition = nextTop + nextHeight;
      if (targetPosition <= top) {
        if (cache) {
          let {
            left: prevLeft,
            top: prevTop,
            width: prevWidth,
            height: prevHeight,
          } = cache;
          let cachePosition = prevTop + prevHeight;
          let nextOverlay = this.getOverlay(left, width, nextLeft, nextWidth);
          let prevOverlay = this.getOverlay(left, width, prevLeft, prevWidth);
          if (nextOverlay > prevOverlay) {
            if (targetPosition >= cachePosition) {
              temp = target;
            } else {
              if (top - targetPosition <= this.failoverDist) {
                temp = target;
              } else {
                temp = cache;
              }
            }
          } else if (nextOverlay === prevOverlay) {
            if (cachePosition > targetPosition) {
              temp = cache;
            } else if (cachePosition === targetPosition) {
              if (nextOverlay === 0) {
                let prevDist = this.rulesSurfaceToPoint(
                  prevLeft,
                  prevTop,
                  prevWidth,
                  prevHeight,
                  prevLeft < left ? left : left + width,
                  top
                );
                let nextDist = this.rulesSurfaceToPoint(
                  nextLeft,
                  nextTop,
                  nextWidth,
                  nextHeight,
                  nextLeft < left ? left : left + width,
                  top
                );
                if (prevDist >= nextDist) {
                  temp = target;
                } else {
                  temp = cache;
                }
              } else {
                if (prevLeft > nextLeft) {
                  temp = target;
                } else {
                  temp = cache;
                }
              }
            } else {
              temp = target;
            }
          } else {
            if (cachePosition >= targetPosition) {
              temp = cache;
            } else {
              if (top - cachePosition <= this.failoverDist) {
                temp = cache;
              } else {
                temp = target;
              }
            }
          }
        } else {
          temp = target;
        }
      }
    } else if (dir === "down") {
      // top值大于焦点元素, 水平方向无交集
      if (nextTop >= top + height) {
        if (cache) {
          let {
            left: prevLeft,
            top: prevTop,
            width: prevWidth,
            height: prevHeight,
          } = cache;
          let nextOverlay = this.getOverlay(left, width, nextLeft, nextWidth);
          let prevOverlay = this.getOverlay(left, width, prevLeft, prevWidth);
          if (nextOverlay > prevOverlay) {
            // target交集更大
            if (nextTop > prevTop) {
              if (nextTop - top - height <= this.failoverDist) {
                temp = target;
              } else {
                if (nextTop < prevTop + prevHeight) {
                  temp = target;
                } else {
                  temp = cache;
                }
              }
            } else {
              temp = target;
            }
          } else if (nextOverlay === prevOverlay) {
            if (nextTop > prevTop) {
              temp = cache;
            } else if (nextTop === prevTop) {
              if (nextOverlay === 0) {
                let prevDist = this.rulesSurfaceToPoint(
                  prevLeft,
                  prevTop,
                  prevWidth,
                  prevHeight,
                  prevLeft < left ? left : left + width,
                  top + height
                );
                let nextDist = this.rulesSurfaceToPoint(
                  nextLeft,
                  nextTop,
                  nextWidth,
                  nextHeight,
                  nextLeft < left ? left : left + width,
                  top + height
                );
                if (prevDist >= nextDist) {
                  temp = target;
                } else {
                  temp = cache;
                }
              } else {
                if (prevLeft > nextLeft) {
                  temp = target;
                } else {
                  temp = cache;
                }
              }
            } else {
              temp = target;
            }
          } else {
            // cache交集更大
            if (prevTop > nextTop) {
              if (prevTop - top - height <= this.failoverDist) {
                temp = cache;
              } else {
                if (prevTop < nextTop + nextHeight) {
                  temp = cache;
                } else {
                  temp = target;
                }
              }
            } else {
              temp = cache;
            }
          }
        } else {
          temp = target;
        }
      }
    }
    return temp;
  }

  /**
   * 移入Views或group计算规则
   * @param {Object} pointer 区域限制对象
   * @param {Object} target 待做对比的焦点对象
   * @param {Object|null} cache 满足条件的最优的焦点元素对象
   * @param {String} dir 焦点移动方向
   * @returns Object 满足条件的最优的焦点对象
   */
  rulesDist(pointer, target, cache, dir = "down") {
    let {
      left: nextLeft,
      top: nextTop,
      width: nextWidth,
      height: nextHeight,
    } = target;
    if (!nextWidth && !nextHeight) {
      return cache;
    }
    let { left, top, width, height } = pointer;
    if (
      nextLeft >= left &&
      nextLeft + nextWidth <= left + width &&
      nextTop >= top &&
      nextTop + nextHeight <= top + height
    ) {
      let temp = null;
      if (cache) {
        let { left: prevLeft, top: prevTop, height: prevHeight } = cache;
        if (dir === "left") {
          if (
            nextTop < prevTop ||
            (nextTop === prevTop && nextLeft > prevLeft)
          ) {
            temp = target;
          } else {
            temp = cache;
          }
        } else if (dir === "right" || dir === "down") {
          if (
            nextTop < prevTop ||
            (nextTop === prevTop && nextLeft < prevLeft)
          ) {
            temp = target;
          } else {
            temp = cache;
          }
        } else if (dir === "up") {
          if (
            nextTop + nextHeight > prevTop + prevHeight ||
            (nextTop + nextHeight === prevTop + prevHeight &&
              nextLeft < prevLeft)
          ) {
            temp = target;
          } else {
            temp = cache;
          }
        }
      } else {
        temp = target;
      }
      return temp;
    } else {
      return cache;
    }
  }

  /**
   * 移入新group的焦点计算方法
   * @param {String} direction 移动方向
   * @param {Object} pointer 焦点元素对象
   */
  rulesInToGroup(direction, pointer) {
    let temp = null;
    let { __pools__, sign } = pointer;
    let list = __pools__.getInnerGroupList(sign);
    if (list.length) {
      list.forEach((val) => {
        temp = this.rulesDist(pointer, val, temp, direction);
      });
      if (temp.name === "group") {
        return this.rulesInToGroup(direction, temp);
      } else if (temp.name === "items") {
        return temp;
      }
    }
  }

  /**
   * 移入新Views的焦点计算方法
   * @param {String} direction 方向
   * @param {Views} views 对应Views对象
   */
  rulesInToViews(direction, views) {
    let temp = null;
    let { __views__ } = views;
    let { viewSign, routeViews, rootPopup } = __views__;
    let child =
      viewSign && routeViews[viewSign] && routeViews[viewSign].__item__
        ? [routeViews[viewSign].__item__]
        : [];
    let tempArr = rootPopup.items.concat(child);
    if (tempArr.length) {
      tempArr.forEach((val) => {
        temp = this.rulesDist(views, val, temp, direction);
      });
      if (temp.name === "views") {
        return this.rulesInToViews(direction, temp);
      } else if (temp.name === "group") {
        return this.rulesInToGroup(direction, temp);
      } else if (temp.name === "items") {
        return temp;
      }
    }
  }

  /**
   * 查找相邻节点规则,如果含有group标记，则仅在对应group内查找相邻节点
   * @param {String} direction 移动方向
   * @param {Object} pointer 焦点元素对象
   * @param {Boolean} inGroup 是否限定在路由区域内移动
   */
  rulesInArea(direction, pointer, inGroup = false) {
    let next;
    let { __list__, __views__, __group__, popup, name } = pointer;
    let child = [];
    if (name === "views") {
      let { $parent } = __views__;
      let layer = $parent.getLayer(popup);
      __list__ = layer ? layer.items : [];
      child = [pointer];
    } else {
      // 获取焦点子view代理对象
      let { viewSign, routeViews } = __views__;
      child =
        viewSign &&
        hasOwn(routeViews, viewSign) &&
        routeViews[viewSign].__item__
          ? [routeViews[viewSign].__item__]
          : [];
    }
    let tempArr = popup ? __list__ : __list__.concat(child);
    if (tempArr.length) {
      tempArr.forEach((value) => {
        if (value !== pointer) {
          next = this.rules(pointer, value, next, direction) || next;
        }
      });
      if (next) {
        if (next.name === "views") {
          if (!inGroup) {
            // 移入进子路由中，移动到对应方向上的第一个可见节点上
            return this.rulesInToViews(direction, next);
          }
        } else if (next.name === "group") {
          return this.rulesInToGroup(direction, next);
        } else if (next.name === "items") {
          return next;
        }
      } else {
        let $parent, __item__;
        if (name === "views") {
          let views = __views__.$parent;
          $parent = views.$parent;
          __item__ = views.__item__;
        } else {
          $parent = __views__.$parent;
          __item__ = __views__.__item__;
        }
        if (__group__) {
          return this.rulesInArea(direction, __group__);
        } else if (!inGroup && !popup && $parent) {
          return this.rulesInArea(direction, __item__);
        }
      }
    }
  }

  /**
   * 查找指定方向上与pointer含有相同标记的下一个焦点
   * @param {String} direction 方向
   * @param {Object} pointer 焦点对象
   * @returns Object
   */
  rulesInSign(direction, pointer) {
    let temp = null;
    let { __sign__ } = pointer;
    if (__sign__.length) {
      __sign__.forEach((val) => {
        val.selected = false;
        if (val != pointer) {
          temp = this.rules(pointer, val, temp, direction);
        }
      });
      if (temp) {
        temp.selected = true;
      } else {
        pointer.selected = true;
      }
      return temp;
    }
  }

  /**
   * 换到下一行第一个
   * @param {Object} pointer 焦点对象
   * @param {Boolean} inGroup 是否限定区域
   * @returns Object|null
   */
  rulesTurnDown(pointer, inGroup = false) {
    let { top, height } = pointer;
    let temper = this.rulesInArea("down", pointer, inGroup);
    if (temper) {
      let tempObj = null;
      while (
        (tempObj = this.rulesInArea("left", temper, inGroup)) &&
        tempObj.top > top + height
      ) {
        temper = tempObj;
      }
    }
    return temper;
  }

  /**
   * 换到上一行最后一个
   * @param {Object} pointer 焦点对象
   * @param {Boolean} inGroup 是否限定区域
   * @returns Object|null
   */
  rulesTurnUp(pointer, inGroup = false) {
    let { top } = pointer;
    let temper = this.rulesInArea("up", pointer, inGroup);
    if (temper) {
      let tempObj = null;
      while (
        (tempObj = this.rulesInArea("right", temper, inGroup)) &&
        tempObj.top + tempObj.height < top
      ) {
        temper = tempObj;
      }
    }
    return temper;
  }

  /**
   * 从列表中查找距离基准坐标最近的焦点
   * @param {Number } x 基准点X坐标
   * @param {Number} y 基准点Y坐标
   * @param {Array} list 焦点列表
   * @returns Object
   */
  rulesPosition(x, y, list) {
    let temp = null;
    if (list.length) {
      list.forEach((val) => {
        temp = this.rulesPoint(x, y, val, temp);
      });
      if (temp.name === "views") {
        let { __list__, __views__ } = temp;
        // 获取焦点子view代理对象
        let { viewSign, routeViews } = __views__;
        let child =
          viewSign && routeViews[viewSign] && routeViews[viewSign].__item__
            ? [routeViews[viewSign].__item__]
            : [];
        let tempArr = __list__.concat(child);
        return this.rulesPosition(x, y, tempArr);
      } else if (temp.name === "group") {
        return this.rulesPosition(x, y, temp.__list__);
      } else if (temp.name === "items") {
        return temp;
      }
    }
  }

  /**
   * 从列表中查找满足条件的焦点
   * @param {Array} list 焦点列表
   * @param {Function} fn 对比条件
   * @returns Object
   */
  ergodicList(list, fn) {
    let temp = null;
    for (let i = 0; i < list.length; i++) {
      const item = list[i];
      if (item.name === "views") {
        let { __list__, __views__ } = item;
        // 获取焦点子view代理对象
        let { viewSign, routeViews } = __views__;
        let child =
          viewSign && routeViews[viewSign]
            ? [routeViews[viewSign].__item__]
            : [];
        let tempArr = __list__.concat(child);
        temp = this.ergodicList(tempArr, fn);
      } else if (item.name === "group") {
        temp = this.ergodicList(item.__list__, fn);
      } else if (item.name === "items") {
        if (fn(item)) {
          temp = item;
        }
      }
      if (temp) {
        break;
      }
    }
    return temp;
  }

  /**
   * 为焦点元素添加类名
   * @param {Object} pointer 焦点元素
   * @param {String} className 类名
   */
  addClass(pointer, className) {
    let tempName = pointer.elem.className;
    let tempArr = tempName.split(/\s+/);
    if (tempArr.indexOf(className) < 0) {
      tempArr.push(className);
    }
    return (pointer.elem.className = tempArr.join(" "));
  }

  /**
   * 移除元素焦点类名
   * @param {Object} pointer 焦点元素
   * @param {String} className 类名
   */
  removClass(pointer, className) {
    let tempName = pointer.elem.className;
    let tempArr = tempName.split(/\s+/);
    tempArr = tempArr.filter((key) => key !== className);
    return (pointer.elem.className = tempArr.join(" "));
  }

  /**
   * 变更按键键值对应关系
   * @param {Object} options 需要调整的按键对象
   * @returns keyMaps
   */
  cnkiMaps(options) {
    for (let [mapKey, mapVal] of Object.entries(options)) {
      let cnkiKey = mapKey.toUpperCase();
      if (hasOwn(this.keyMaps, cnkiKey)) {
        let mapList;
        if (typeof mapVal === "number") {
          mapList = [mapVal];
        } else if (mapVal instanceof Array) {
          mapList = mapVal;
        } else {
          continue;
        }
        mapList.forEach((val) => {
          for (let [key, list] of Object.entries(this.keyMaps)) {
            let index = list.indexOf(val);
            if (key === cnkiKey && index < 0) {
              this.keyMaps[key].push(val);
            } else if (key !== cnkiKey && index >= 0) {
              this.keyMaps[key].splice(index, 1);
            }
          }
        });
      }
    }
    return this.keyMaps;
  }
}
